#pragma once

#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include "socket.hpp"

#define ErrorHtmlPath "webroot/"

std::string ErrorPath(ErrorHtmlPath);

const std::string sep = "\r\n";
const std::string Webroot = "./webroot";

class HttpServer;

struct PthreadData
{
    PthreadData(int fd, HttpServer *s)
        : sockfd(fd),
          svr(s)
    {}
    int sockfd;
    HttpServer *svr;
};

class HttpRequest
{
public:
    HttpRequest()
    {
    }
    ~HttpRequest()
    {
    }

public:

    // 反序列化,将请求的字符串转化为结构体成员
    bool Deserialize(std::string package)
    {
        while (true)
        {
            size_t pos = package.find(sep);
            if (pos == std::string::npos)
                return false;
            std::string temp = package.substr(0, pos);
            // 如果发现为空,说明是空报头,空报头的下一个是请求正文
            if (temp.empty())
                break;
            req_header.push_back(temp);
            package.erase(0, pos + sep.size());
        }
        package.erase(0, sep.size());
        // 此时package只剩下请求正文
        text = package;
    }

    // 解析请求行,获得想要访问的资源和其他基本信息
    void Parse()
    {
        // stringstream的使用
        // 能够以空格为分隔符,将字符串提取出来
        std::stringstream ss(req_header[0]);
        // method url http_version都能获取到请求行里的对应字段
        ss >> method >> url >> http_version;

        file_path = Webroot; // ./wwwroot

        // 如果客户端不指定填写访问文件,HTTP协议会默认加上/
        if (url == "/" || url == "/index.html")
        {
            // 默认用户直接访问"首页"
            file_path += "/index.html";
        }
        // 如果客户端指定了访问的资源,就原封不动地解析给file_path
        // 资源有没有错误交给其他模块判断
        else
        {
            file_path += url;
        }

        // 再来个文件后缀的判断功能,结构体成员suffix
        auto pos = file_path.rfind(".");
        if(pos == std::string::npos) suffix = ".html"; // 默认是html
        else suffix = file_path.substr(pos);
    }



    void DebugResponse()
    {
        cout << "===========================================" << endl;
        printf("解析后的资源: method: %s, url: %s, 实际访问资源: %s, http_version: %s\n", method.c_str(), url.c_str(), file_path.c_str(), http_version.c_str());
        cout << "===========================================" << endl;
    }

public:
    std::vector<std::string> req_header;
    std::string text;

    // 解析之后的结果
    std::string method;
    std::string url;
    std::string http_version;
    std::string file_path; // ./wwwroot/a/b/c.html 2.png

    std::string suffix;
};

class HttpServer
{
public:
    HttpServer(uint16_t port)
        : port_(port)
    {
        // 第一个是文本的类型
        // 第二个是图片的类型
        // 通过后缀去判定是什么类型
        content_type.insert({".html", "text/html"});
        content_type.insert({".png", "image/png"});
    }

    ~HttpServer()
    {}

public:
    void Init()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
    }
    static void *ThreadRun(void *args)
    {
        pthread_detach(pthread_self());
        PthreadData *td = static_cast<PthreadData *>(args);
        td->svr->HandlerHttp(td->sockfd);
        delete td;
        return nullptr;
    }

    void HandlerHttp(int sockfd)
    {
        char buffer[10204];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer), 0);
        if (n > 0)
        {
            buffer[n] = 0;
            cout << "read's n is :" << n << "请求过来的报文为: " << buffer << endl;
            std::string package = buffer;
            HttpRequest req;

            req.Deserialize(package);
            req.Parse();

            bool OK = true;
            // ReadHtmlContent应该有判断访问路径是否合理的功能
            // 如果不合法,就返回空字符串给text
            std::string text = ReadHtmlContent(req.file_path);
            // 如果text为空,说明服务端发现请求有问题,将请求判断为不合理
            if(text.empty()) 
            {
                OK = false;
                std::string error_html = Webroot;
                error_html += "/";
                error_html += "Error.HTML";
                // 将text的内容转变为Error.HTML里的错误显示信息
                // 给用户看到错误的提示页面
                text = ReadHtmlContent(error_html);
            }

            // 对请求要做出响应,响应的制作过程:
            std::string blank_line = "\r\n";  // 类似换行符
            std::string response_line;
            if(OK)
                response_line = "HTTP/1.0 200 OK\r\n"; // 请求行
            else
                response_line = "HTTP/1.0 404 Not Found\r\n";

            std::string response_header = "Content-Length: ";  // K-V属性

            response_header += std::to_string(text.size());
            response_header += blank_line;
            
            // 添加文件的属性,例如图片一类
            response_header += "Content-Type: ";
            response_header += SuffixToDesc(req.suffix);
            response_header += blank_line;

            // 添加cookie
            response_header += "Set-Cookie: name=haha&&passwd=12345";
            response_header += blank_line;

            // 将内容都加进content里
            std::string content = response_line;
            content += response_header;
            content += blank_line;
            content += text;

            send(sockfd, content.c_str(), content.size(), 0);
            // cout << "本次服务端发送的报文原样:" << content.c_str() << endl;
            // send(sockfd, content.c_str(), content.size(), 0);
            // req.DebugResponse();
            // V2版本
            // 分离出Http的请求行中的URL
            // size_t pos = package.find(" ");
            // size_t nextpos = package.find(" ", pos + 1);
            // std::string url = Webroot;                         // URL的根目录要指定
            // url += package.substr(pos + 1, nextpos - pos - 1); // 此时已经获得URL,知道是要获取哪个文件资源
            // cout << "URL IS : " << url.c_str() << endl;
            // // 获得指定文件的内容
            // std::string text = ReadHtmlContent(url);
            // cout << "Text: " <<text.c_str() << endl;
            // //对请求要做出响应,响应的制作过程:
            // std::string blank_line = "\r\n";  // 类似换行符
            // std::string response_line = "HTTP/1.0 200 OK\r\n"; // 请求行
            // std::string response_header = "Content-Length: ";  // K-V属性
            // response_header += std::to_string(text.size());
            // response_header += blank_line;
            // // 将内容都加进content里
            // std::string content = response_line;
            // content += response_header;
            // content += blank_line;
            // content += text;
            // cout << "本次服务端发送的报文原样:" << content.c_str() << endl;
            // send(sockfd, content.c_str(), content.size(), 0);
        }
    }

    // V1版本
    // static void *PthreadTask(void* arg)
    // {
    //     PthreadData* sp = static_cast<PthreadData*>(arg);
    //     // 分离子线程
    //     pthread_detach(pthread_self());
    //     char buffer[10204];
    //     ssize_t n = recv(sp->sockfp_, buffer, sizeof(buffer), 0);
    //     if(n > 0)
    //     {
    //         buffer[n] = 0;
    //         std::cout << buffer;
    //         //对请求要做出响应,响应的制作过程:
    //         std::string blank_line = "\r\n";  // 类似换行符
    //         std::string text = "Hello World";
    //         std::string response_line = "HTTP/1.0 200 OK\r\n"; // 请求行
    //         std::string response_header = "Content-Length: ";  // K-V属性
    //         response_header += std::to_string(text.size());
    //         response_header += blank_line;
    //         // 将内容都加进package里
    //         std::string package = response_line;
    //         package += response_header;
    //         package += blank_line;
    //         package += text;
    //         cout << "本次服务端发送的数据原样:" << package << endl;
    //         send(sp->sockfp_, package.c_str(), package.size(), 0);
    //     }
    // }

    // 以二进制方式读取一个文件里的所有内容
    // 如果读取的文件不存在,返回一个空的string
    static std::string ReadHtmlContent(const std::string &htmlPath)
    {
        // 向htmlPath文件里读取内容,htmlPath由用户自己选择
        // std::ifstream in(htmlPath);
        // in会尝试打开文件,如果文件不存在就打开失败
        // 打开失败的文件说明给的字符串有问题,返回一个空字符串以提示上层
        // if (!in.is_open())
        // {
        //     lg(Warning, "Open HtmlPath Error!!!");
        //     return "";
        // }
        // std::string content;
        // std::string line;
        // while (getline(in, line))
        // {
        //     content += line;
        // }
        // in.close();
        // 以上方法对于图片这类二进制文件无法读取

        std::ifstream in(htmlPath, std::ios::binary); // 以二进制方式读
        if(!in.is_open()) return "";

        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);

        std::string content;
        content.resize(len);

        in.read((char*)content.c_str(), content.size());

        return content;
    }

    void Start()
    {
        std::string inbuffer_stream;
        while (true)
        {
            std::string ip;
            uint16_t port;
            int sockfp = listensock_.Accept(&ip, &port);
            if (sockfp < 0)
                break;
            PthreadData *pdata = new PthreadData(sockfp, this);
            pthread_t tip;
            pthread_create(&tip, nullptr, ThreadRun, (void *)pdata);
            // 主线程关闭新开的管道文件
            // close(sockfp); 线程的文件是共享的,不能随便关闭
        }
        listensock_.Close();
    }

    // 返回一个文件的类型
    std::string SuffixToDesc(const std::string &suffix)
    {
        auto iter = content_type.find(suffix);
        // 判断该后缀有没有对应的类型
        if(iter == content_type.end()) return content_type[".html"]; // 找不到,默认返回文本类型
        else return content_type[suffix];
     }
private:
    uint16_t port_;
    Sock listensock_;
    std::unordered_map<std::string, std::string> content_type; // 用来存放每个后缀对应的类型
};